package org.hefan.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.hefan.basic.exception.BusinessException;
import org.hefan.basic.service.impl.BaseServiceImpl;
import org.hefan.basic.util.*;
import org.hefan.user.constant.Constant;
import org.hefan.user.constant.WxConstants;
import org.hefan.user.domain.Logininfo;
import org.hefan.user.domain.User;
import org.hefan.user.domain.WxUser;
import org.hefan.user.dto.LoginDto;
import org.hefan.user.dto.PhoneLoginDto;
import org.hefan.user.mapper.LogininfoMapper;
import org.hefan.user.mapper.UserMapper;
import org.hefan.user.mapper.WxUserMapper;
import org.hefan.user.service.ILogininfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Override
    public AjaxResult accountLogin(LoginDto loginDto) {
        String username = loginDto.getUsername();
        String password = loginDto.getPassword();
        Integer loginType = loginDto.getLoginType();

        //1.校验
        //1.1.空值校验
        if (StringUtils.isEmpty(username)
                || StringUtils.isEmpty(password)
                || StringUtils.isEmpty(loginType)) {
            throw new BusinessException("请输入合法信息!!!");
        }

        //1.2.用户名或密码【加密加盐】校验
        Logininfo logininfo = logininfoMapper.findByUserName(loginDto);
        if (logininfo == null) {
            throw new BusinessException("用户名或密码错误!!!");
        }

        String dbMd5Pwd = logininfo.getPassword();
        String md5Pwd = MD5Utils.encrypByMd5(password + logininfo.getSalt());
        if (!dbMd5Pwd.equals(md5Pwd)) {
            throw new BusinessException("用户名或密码错误!!!");
        }

        //1.3.状态校验 - 启用，禁用
        if (!logininfo.getDisable()) {
            throw new BusinessException("你的账号被冻结，请联系管理员!!!");
        }

        //======================== 校验完毕 ================================
        //2.将token和登录信息对象 保存在 redis 30分钟有效
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo, 30,  TimeUnit.MINUTES);

        //3.使用Map将token和登录对象绑定到AjaxResult的resultObj中响应给前端
        //说明：token相当于jessionid   登录对象响应到前端 - 在系统中回显用户名
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        map.put("logininfo", logininfo);

        return AjaxResult.me().setResultObj(map);
    }

    @Override
    public AjaxResult wxUserLogin(Map<String, String> map) {
        // 1.获取code
        String code = map.get("code");
        // 2.使用code 通过HttpClient 发送请求 获取access_token - 返回json字符串
        String url2 = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code);
        String jsonStr = HttpClientUtils.httpGet(url2);
        //System.out.println(jsonStr);
        //3.使用fastJson阿里巴巴的 转成json对象 - 获取对象的中的数据【access_token or openid】
        JSONObject jsonObject = JSONObject.parseObject(jsonStr); // 此时jsonObject就是json对象
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        //4.判断是否绑定
        WxUser wxUser = wxUserMapper.findByOpenId(openid);
        //4.1.已经绑定 - 免密登录
        if (wxUser != null && wxUser.getUser_id() != null) {
            Long user_id = wxUser.getUser_id();
            Logininfo logininfo = logininfoMapper.findByUserId(user_id);

            //2.将token和登录信息对象 保存在 redis 30分钟有效
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
            //3.使用Map将token和登录对象绑定到AjaxResult的resultObj中响应给前端
            //说明：token相当于jessionid   登录对象响应到前端 - 在系统中回显用户名
            Map<String,Object> wxMap = new HashMap<>();
            wxMap.put("token",token);
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            wxMap.put("logininfo",logininfo);

            return AjaxResult.me().setResultObj(wxMap);
        } else {
            //4.2.没有绑定 - 将access_token or openid响应给前端
            Map<String,Object> wxMap = new HashMap<>();
            wxMap.put("access_token",access_token);
            wxMap.put("openid",openid);

            return AjaxResult.me().setSuccess(false).setResultObj(wxMap);
        }
    }

    @Override
    public AjaxResult wxUserBinder(Map<String, String> map) {
        // 从map中获取前端传来的数据
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String accessToken = map.get("accessToken");
        String openId = map.get("openId");
        //1.校验
        //1.1.空值校验
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)) {
            throw new BusinessException("请输入正确的数据!!!");
        }
        //1.2.验证码 - 过期校验
        Object obj = redisTemplate.opsForValue().get(Constant.BINDER_CODE_PREFIX + phone);
        if (obj == null) {
            throw new BusinessException("验证码无效，请重新获取!!!");
        }
        //1.3.验证码 - 是否正确
        String codeTmp = obj.toString().split(":")[0];
        if (!codeTmp.equals(verifyCode)) {
            throw new BusinessException("验证码错误!!!");
        }

        //2.绑定
        //2.1.获取WxUser对象
        // 通过死的GET_USER_URL替换ACCESS_TOKEN与OPENID得到GET_USER_URL
        String url3 = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        // 后端发送http请求得到wxUser对象的json字符串
        String wxUserStr = HttpClientUtils.httpGet(url3);
        // 把wxUser对象的json字符串转换成wxUser对象
        WxUser wxUser = wxUserStr2WxUser(wxUserStr);

        //2.2.获取User对象
        // 通过前端传来的电话号找到user对象
        User user = userMapper.findByPhone(phone);
        // 如果找不到这个user对象,表示用户没有绑定user对象
        if (user == null) {
            // 把phone转换成user对象,用户名就是phone
            user = phone2User(phone);
            // 再把user对象转换成logininfo对象
            Logininfo logininfo = user2Logininfo(user);
            // 保存logininfo,返回logininfo_id - 自增主键
            logininfoMapper.save(logininfo);
            // 把logininfo的id设置给user的Logininfo_id
            user.setLogininfo_id(logininfo.getId());
            // 再保存user对象
            userMapper.save(user); // 自增主键 - user对象就有id了
        }
        if (user.getState() == 0) {
            throw new BusinessException("账号已被封禁,请联系管理员!!!");
        }

        //2.3.将User对象与WxUser对象绑定 = t_wxuser表中设置一个user_id
        // 从user对象得到user的id设置给wxUser对象的User_id,把user和wxUser关联起来
        wxUser.setUser_id(user.getId());
        wxUserMapper.save(wxUser);

        //3.免密登录
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfo_id());
        //2.将token和登录信息对象 保存在 redis 30分钟有效
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
        //3.使用Map将token和登录对象绑定到AjaxResult的resultObj中响应给前端
        //说明：token相当于jessionid   登录对象响应到前端 - 在系统中回显用户名
        Map<String,Object> wxMap = new HashMap<>();
        wxMap.put("token",token);
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        wxMap.put("logininfo",logininfo);

        return AjaxResult.me().setResultObj(wxMap);
    }

    @Override
    public AjaxResult phoneLogin(PhoneLoginDto phoneLoginDto, HttpServletRequest request) {
        String phone = phoneLoginDto.getPhone();
        Integer loginType = phoneLoginDto.getLoginType();
        String verifycode = phoneLoginDto.getVerifycode();
        //1.校验
        //1.1.空值校验
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(loginType)) {
            throw new BusinessException("请输入合法信息!!!");
        }

        String obj = (String)redisTemplate.opsForValue().get(Constant.PHONE_LOGIN_CODE_PREFIX + phone);
        String verifycodeBySend = obj.split(":")[0];
        if (verifycode.equals(verifycodeBySend)) {
            Logininfo logininfo = logininfoMapper.findByPhone(phoneLoginDto);
            //======================== 校验完毕 ================================
            //2.将token和登录信息对象 保存在 redis 30分钟有效
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(token, logininfo, 30,  TimeUnit.MINUTES);

            //3.使用Map将token和登录对象绑定到AjaxResult的resultObj中响应给前端
            //说明：token相当于jessionid   登录对象响应到前端 - 在系统中回显用户名
            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            map.put("logininfo", logininfo);

            return AjaxResult.me().setResultObj(map);
        } else {
            return AjaxResult.me().setSuccess(false);
        }
    }


    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);//用户
        return logininfo;
    }

    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        //密码：随机
        String md5Pwd = MD5Utils.encrypByMd5(StrUtils.getComplexRandomString(6) + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }

    private WxUser wxUserStr2WxUser(String wxUserStr) {
        WxUser wxUser = new WxUser();
        // 通过阿里巴巴提供的方法,把json字符串转换成json对象,这样才能获取到其中的数据
        JSONObject jsonObject = JSONObject.parseObject(wxUserStr);
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        String address = jsonObject.getString("country") + jsonObject.getString("province") + jsonObject.getString("city");
        wxUser.setAddress(address);
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }
}
